package gupao.springboot.redis.service;

import gupao.springboot.redis.dto.Product;
import gupao.springboot.redis.dto.ProductStatics;
import gupao.springboot.redis.mapper.ProductMapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class ProductService {
    @Resource
    private ProductMapper productMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedissonClient redissonClient1;

    private static String script = "" +
            "local lockSet = redis.call('exists', KEYS[1])\n" +
            "if lockSet == 0 then\n" +
            "redis.call('set', KEYS[1], ARGV[1])\n" +
            "redis.call('expire', KEYS[1], ARGV[2])\n" +
            "end\n" +
            "return lockSet\n";


    public Long getId() {
        return redisTemplate.opsForValue().increment("productId");
    }

    public List<Product> selectProduct() {
        String key = "productList";
        List<Product> products = (List<Product>) redisTemplate.opsForValue().get(key);
        if (products == null) {
            synchronized (ProductService.class) {
                products = (List<Product>) redisTemplate.opsForValue().get(key);
                if (products == null) {
                    products = productMapper.selectProduct();
                    redisTemplate.opsForValue().set(key, products);
                }
            }
        }
        return products;
    }


    public String pay(Long orderId) {
        try {
            Long lock = (Long) redisTemplate.execute(RedisScript.of(script, Long.class), Arrays.asList("pay:" + orderId), "1", 30);
            if (lock == 0) {
                //模拟支付业务代码执行10s
                Thread.sleep(10000);
                //处理完业务逻辑删除锁  异常了
                redisTemplate.delete("pay:" + orderId);
            }
            return lock == 0 ? "正常支付完毕" : "请稍等，已经有人在支付！！";
        } catch (Exception exception) {
            redisTemplate.delete("lock" + orderId);
            return "系统异常";
        }
    }

    public String payRedisson(Long orderId) throws InterruptedException {
        RLock rLock1 = redissonClient.getLock("order_lock" + orderId);
        if (rLock1.tryLock(10, -1, TimeUnit.SECONDS)) {
            System.out.println("获取锁成功");
            Thread.sleep(10000);
            rLock1.unlock();
            return "处理完成";
        } else {
            System.out.println("获取锁失败");
            return "请稍等，已经有人在支付！！";
        }
    }


    public String payMultiLock(Long orderId) throws InterruptedException {
        RLock rLock = redissonClient.getLock("order_lock" + orderId);
        RLock rLock1 = redissonClient1.getLock("order_lock" + orderId);
        RLock lock = redissonClient.getMultiLock(rLock, rLock1);
        if (lock.tryLock(-1 - 1, TimeUnit.SECONDS)) {
            System.out.println("获取锁成功");
            Thread.sleep(300000);
            lock.unlock();
            return "处理完成";
        } else {
            System.out.println("获取锁失败");
            return "请稍等，已经有人在支付！！";
        }
    }

    public Integer addPv(Integer id) {
        return redisTemplate.opsForHash().increment("productStatics:" + id, "pv", 1).intValue();
    }

    public Integer adduv(Integer id) {
        return redisTemplate.opsForHash().increment("productStatics:" + id, "uv", 1).intValue();
    }


    public void rankInit() {
        List<ProductStatics> productStaticsList = productMapper.selectStaticsAll();
        Set<ZSetOperations.TypedTuple<Integer>> tuples = new HashSet<>();
        productStaticsList.forEach(p -> {
            ZSetOperations.TypedTuple typedTuple = new DefaultTypedTuple(p.getId(), Integer.valueOf(p.getPv()).doubleValue());
            tuples.add(typedTuple);
        });
        redisTemplate.opsForZSet().add("productRank", tuples);
    }


    public Set rank() {
        return redisTemplate.opsForZSet().reverseRange("productRank", 0, -1);
    }

    public long test() {
        long start = System.currentTimeMillis();
        List<Object> list = redisTemplate.executePipelined((RedisCallback<Object>) connection ->
        {
            for (int i = 0; i < 1000; i++) {
                connection.stringCommands().set(("key" + i).getBytes(), String.valueOf(i).getBytes(StandardCharsets.UTF_8));
            }
            return null;
        });
        long end = System.currentTimeMillis();
        return end - start;
//        return redisTemplate.opsForZSet().reverseRange("productRank",0,-1);
    }

    public long test1() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            redisTemplate.opsForValue().set("key" + i, i);
        }
        long end = System.currentTimeMillis();
        return end - start;
    }


}

